<!--
  文件管理页面 - file/index.vue
  
  功能特性：
  1. 支持单文件和多文件上传
  2. 当 multiple=true 时，支持批量上传图片
  3. 使用 Promise.all 进行并发批量上传
  4. 实时显示上传进度
  5. 支持拖拽上传
  6. 文件预览和管理
  7. 无需二次确认，选择文件后直接上传
  
  使用方法：
  1. 作为独立页面使用：
     <FileManager />
  
  2. 作为选择器使用：
     <FileManager 
       :selectable="true" 
       :multiple="true" 
       @file-selected="handleFileSelected"
     />
  
  Props:
  - selectable: 是否可选择文件，默认 false
  - multiple: 是否支持多文件选择，默认 false
  - selectedFiles: 已选中的文件列表
  
  Events:
  - file-selected: 文件选择变化时触发
  
  上传逻辑：
  - 单文件模式：选择文件后立即上传
  - 多文件模式：选择文件后自动开始批量上传
  - 选择模式：上传成功后自动选中文件并确认返回
-->

<template>
  <div>
    <SearchForm
        @search="onSearch"
        @reset="onReset"
        :search-list="searchList" :search-params="searchParams">

      <template v-slot:button>
        <el-button type="primary" size="mini" icon="el-icon-upload" @click="showUploadDialog">上传文件</el-button>
      </template>
    </SearchForm>

    <div class="file-layout">
      <el-card shadow="never">

        <div class="file-grid" v-loading="loading">
          <template v-if="tableData.length > 0">
            <div
              v-for="file in tableData"
              :key="file.id"
              class="file-item"
              :class="{'file-selected': selectable && isSelected(file)}"
              @click="selectable && selectFile(file)"
            >
              <!-- 图片文件 -->
              <div v-if="isImageFile(file.file_type)" class="file-preview">
                <el-image
                  :src="file.file_url"
                  fit="cover"
                  :preview-src-list="[file.file_url]"
                ></el-image>
              </div>

              <!-- 音频文件 -->
              <div v-else-if="isAudioFile(file.file_type)" class="file-preview audio-preview">
                <i class="el-icon-headset"></i>
                <audio :src="file.file_url" controls class="audio-player"></audio>
              </div>

              <!-- 视频文件 -->
              <div v-else-if="isVideoFile(file.file_type)" class="file-preview video-preview">
                <i class="el-icon-video-camera"></i>
                <video :src="file.file_url" controls class="video-player"></video>
              </div>

              <!-- 其他文件 -->
              <div v-else class="file-preview other-preview">
                <i class="el-icon-document"></i>
              </div>

              <div class="file-info">
                <div class="file-name" :title="file.file_origin_name">{{ truncateFilename(file.file_origin_name) }}</div>
                <div class="file-meta">
                  <span>{{ formatFileSize(file.file_size) }}</span>
                  <span>{{ formatDate(file.create_time) }}</span>
                </div>
                <div class="file-actions">
                  <el-tooltip content="复制链接" placement="top">
                    <i class="el-icon-copy-document" @click="copyFileUrl(file.file_url)"></i>
                  </el-tooltip>
                  <el-tooltip content="下载" placement="top">
                    <a :href="file.file_url" target="_blank" download><i class="el-icon-download"></i></a>
                  </el-tooltip>
                  <el-tooltip content="删除" placement="top">
                    <i class="el-icon-delete" @click="onDelete(file)"></i>
                  </el-tooltip>
                </div>
              </div>
            </div>
          </template>

          <div v-if="tableData.length === 0 && !loading" class="no-files">
            <i class="el-icon-folder-opened"></i>
            <p>没有文件</p>
          </div>
        </div>
      </el-card>

      <div class="page-box">
        <el-pagination
            background
            :current-page="pageNo"
            :page-sizes="pageSizes"
            :page-size="pageSize"
            layout="total, sizes, prev, pager, next, jumper"
            :total="totalSize"
            @size-change="changePageSize"
            @current-change="changePageNo"
        />
      </div>
    </div>

    <!-- 上传文件对话框 -->
    <el-dialog
      title="上传文件"
      :append-to-body="true"
      :visible.sync="uploadDialogVisible"
      width="600px"
      :close-on-click-modal="false"
    >
      <el-upload
        v-if="uploadDialogVisible"
        class="upload-container"
        drag
        action=""
        :http-request="handleFileUpload"
        :before-upload="beforeUpload"
        :limit="multiple ? 10 : 1"
        :multiple="multiple"
        :show-file-list="true"
        :file-list="uploadFileList"
        :on-change="handleFileChange"
        :on-remove="handleFileRemove"
        :auto-upload="false"
      >
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处，或<em>点击上传</em>
          <br>
          <small v-if="multiple">支持多文件上传，最多10个文件</small>
          <br>
          <small v-if="selectable" style="color: #409eff;">上传成功后将自动选中文件</small>
        </div>
      </el-upload>

      <!-- 上传进度显示 -->
      <div v-if="uploadProgress.length > 0" class="upload-progress">
        <div v-for="(progress, index) in uploadProgress" :key="index" class="progress-item">
          <div class="progress-info">
            <span class="filename">{{ progress.filename }}</span>
            <span :class="['status', progress.status]">
              {{ progress.status === 'uploading' ? '上传中' : progress.status === 'success' ? '成功' : '失败' }}
            </span>
          </div>
          <el-progress 
            :percentage="progress.percentage" 
            :status="progress.status === 'error' ? 'exception' : progress.status === 'success' ? 'success' : ''"
          />
        </div>
      </div>

      <span slot="footer" class="dialog-footer">
        <el-button @click="uploadDialogVisible = false">取 消</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import crud from "@/mixins/crud";
import { http, uploadFile } from "@/api";
import dayjs from 'dayjs';

export default {
  name: "file",
  mixins: [crud],
  props: {
    selectable: {
      type: Boolean,
      default: false
    },
    multiple: {
      type: Boolean,
      default: false
    },
    selectedFiles: {
      type: Array,
      default: () => []
    }
  },
  data() {
    return {
      request: http.file,
      uploadDialogVisible: false,
      searchList: [
        {
          prop: 'file_origin_name',
          label: '文件名称',
          name: 'input',
          placeholder: '文件名称'
        },
      ],
      searchParams: {
        file_origin_name: '',
      },
      loading: false,
      pageNo: 1,
      pageSize: 10,
      pageSizes: [10, 20, 30, 40, 50],
      totalSize: 0,
      tableData: [],
      // 内部选中的文件列表
      internalSelectedFiles: [],
      // 批量上传相关
      uploadFileList: [], // 用于 el-upload 的 file-list
      uploadProgress: [], // 用于显示上传进度
      batchUploading: false, // 批量上传时的加载状态
      // 上传成功后的文件列表
      uploadedFiles: []
    }
  },
  created() {
    // 初始化内部选中的文件列表
    if (this.selectedFiles && this.selectedFiles.length > 0) {
      this.internalSelectedFiles = [...this.selectedFiles];
    }
  },
  methods: {
    // 文件类型判断
    isImageFile(type) {
      return type && (type.startsWith('image/') || type === 'image');
    },
    isAudioFile(type) {
      return type && (type.startsWith('audio/') || type === 'audio');
    },
    isVideoFile(type) {
      return type && (type.startsWith('video/') || type === 'video');
    },

    // 格式化文件大小
    formatFileSize(bytes) {
      if (!bytes || bytes === 0) return '0 B';
      const k = 1024;
      const sizes = ['B', 'KB', 'MB', 'GB'];
      const i = Math.floor(Math.log(bytes) / Math.log(k));
      return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
    },

    // 格式化日期
    formatDate(date) {
      return dayjs(date).format('YYYY-MM-DD');
    },

    // 截断文件名
    truncateFilename(filename) {
      if (!filename) return '';
      if (filename.length <= 15) return filename;
      const ext = filename.lastIndexOf('.') > 0 ? filename.substring(filename.lastIndexOf('.')) : '';
      const name = filename.substring(0, filename.length - ext.length);
      return name.substring(0, 10) + '...' + (ext ? ext : '');
    },

    // 复制文件URL
    copyFileUrl(url) {
      const input = document.createElement('input');
      input.setAttribute('value', url);
      document.body.appendChild(input);
      input.select();
      document.execCommand('copy');
      document.body.removeChild(input);
      this.$message.success('链接已复制到剪贴板');
    },

    // 显示上传对话框
    showUploadDialog() {
      this.uploadDialogVisible = true;
      this.uploadFileList = []; // 清空上传列表
      this.uploadProgress = []; // 清空进度显示
    },

    // 上传前验证
    beforeUpload(file) {
      // const maxSize = 50 * 1024 * 1024; // 50MB
      // if (file.size > maxSize) {
      //   this.$message.error('文件大小不能超过 50MB');
      //   return false;
      // }
      return true;
    },

    // 处理文件上传（单文件模式直接上传）
    async handleFileUpload({ file }) {
      if (!this.multiple) {
        // 单文件模式：直接上传
        try {
          this.loading = true;
          const response = await uploadFile(file);
          if (response && response.data) {
            this.$message.success('文件上传成功');
            
            // 如果是选择模式，自动选中上传的文件
            if (this.selectable) {
              const uploadedFile = {
                id: response.data.id,
                file_url: response.data.file_url,
                file_origin_name: file.name,
                file_size: file.size,
                file_type: file.type,
                create_time: new Date().toISOString()
              };
              
              // 自动选中上传的文件
              this.selectFile(uploadedFile);
              
              // 触发选择事件
              this.$emit('file-selected', this.internalSelectedFiles);
            }
            
            this.uploadDialogVisible = false;
            this.initData(); // 重新加载文件列表
          }
        } catch (error) {
          console.error('上传失败:', error);
          this.$message.error('文件上传失败: ' + (error.message || '未知错误'));
        } finally {
          this.loading = false;
        }
      }
      // 多文件模式：由 handleFileChange 触发批量上传
    },

    // 文件选择相关方法
    selectFile(file) {
      if (this.multiple) {
        // 多选模式
        const index = this.internalSelectedFiles.findIndex(f => f.id === file.id);
        if (index > -1) {
          // 如果已经选中，则取消选择
          this.internalSelectedFiles.splice(index, 1);
        } else {
          // 否则添加到选中列表
          this.internalSelectedFiles.push(file);
        }
      } else {
        // 单选模式
        this.internalSelectedFiles = [file];
      }

      // 触发选择事件
      this.$emit('file-selected', this.internalSelectedFiles);
    },

    // 判断文件是否被选中
    isSelected(file) {
      return this.internalSelectedFiles.some(f => f.id === file.id);
    },

    // 获取选中的文件列表
    getSelectedFiles() {
      return this.internalSelectedFiles;
    },

    // 处理 el-upload 的 file-list 变化
    handleFileChange(file, fileList) {
      this.uploadFileList = fileList;
      
      // 如果是多文件模式，选择文件后自动开始批量上传
      if (this.multiple && fileList.length > 0) {
        this.startBatchUpload();
      }
    },

    // 处理 el-upload 的文件移除
    handleFileRemove(file, fileList) {
      this.uploadFileList = fileList;
    },

    // 开始批量上传
    async startBatchUpload() {
      if (this.uploadFileList.length === 0) {
        this.$message.warning('请先选择要上传的文件');
        return;
      }

      this.batchUploading = true;
      this.uploadProgress = []; // 清空进度显示

      try {
        // 初始化进度显示
        this.uploadFileList.forEach((file, index) => {
          this.uploadProgress[index] = {
            filename: file.name,
            status: 'uploading',
            percentage: 0
          };
        });

        // 使用 Promise.all 进行批量上传
        const uploadPromises = this.uploadFileList.map(async (file, index) => {
          try {
            // 模拟上传进度
            const progressInterval = setInterval(() => {
              if (this.uploadProgress[index].percentage < 90) {
                this.uploadProgress[index].percentage += 10;
              }
            }, 200);

            const response = await uploadFile(file.raw);
            
            clearInterval(progressInterval);
            
            if (response && response.data) {
              this.uploadProgress[index] = {
                filename: file.name,
                status: 'success',
                percentage: 100
              };
              
              return {
                name: file.name,
                url: response.data.file_url || response.data.url,
                status: 'success',
                response: response.data
              };
            } else {
              this.uploadProgress[index] = {
                filename: file.name,
                status: 'error',
                percentage: 0
              };
              
              return {
                name: file.name,
                url: null,
                status: 'error',
                response: response
              };
            }
          } catch (error) {
            this.uploadProgress[index] = {
              filename: file.name,
              status: 'error',
              percentage: 0
            };
            
            console.error(`文件 ${file.name} 上传失败:`, error);
            throw error;
          }
        });

        const uploadedFiles = await Promise.all(uploadPromises);
        
        // 统计成功和失败的文件
        const successFiles = uploadedFiles.filter(file => file.status === 'success');
        const failedFiles = uploadedFiles.filter(file => file.status === 'error');
        
        if (successFiles.length > 0) {
          this.$message.success(`成功上传 ${successFiles.length} 个文件${failedFiles.length > 0 ? `，${failedFiles.length} 个文件上传失败` : ''}`);
          
          // 如果是选择模式，自动选中上传成功的文件
          if (this.selectable) {
            const uploadedFiles = successFiles.map(file => ({
              id: file.response.id,
              file_url: file.url,
              file_origin_name: file.name,
              file_size: file.response.file_size || 0,
              file_type: file.response.file_type || 'application/octet-stream',
              create_time: new Date().toISOString()
            }));
            
            // 根据多选模式处理选中文件
            if (this.multiple) {
              // 多选模式：添加到现有选中列表
              this.internalSelectedFiles = [...this.internalSelectedFiles, ...uploadedFiles];
            } else {
              // 单选模式：替换现有选中列表
              this.internalSelectedFiles = uploadedFiles.length > 0 ? [uploadedFiles[0]] : [];
            }
            
            // 触发选择事件
            this.$emit('file-selected', this.internalSelectedFiles);
          }
          
          this.uploadDialogVisible = false;
          this.initData(); // 重新加载文件列表
        } else {
          this.$message.error('所有文件上传失败');
        }
        
        // 无论成功还是失败，都清空上传列表
        this.uploadFileList = [];
        this.uploadProgress = [];
        
      } catch (error) {
        console.error('批量上传失败:', error);
        this.$message.error('批量上传失败: ' + (error.message || '未知错误'));
      } finally {
        this.batchUploading = false;
      }
    }
  }
}
</script>

<style scoped lang="scss">
.file-layout {
  margin-top: 20px;
}

.file-handle {
  margin-bottom: 20px;
  display: flex;
  justify-content: flex-start;
  gap: 10px;
  align-items: center;

  .selection-tip {
    margin-left: auto;
    color: #909399;
    font-size: 13px;
  }
}

.file-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 15px;
  min-height: 300px;
}

.file-item {
  width: 150px;
  height: 180px;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  overflow: hidden;
  transition: all 0.3s;
  position: relative;
  background-color: #fff;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);

  &:hover {
    transform: translateY(-3px);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);

    .file-actions {
      opacity: 1;
    }
  }

  &.file-selected {
    border: 2px solid #409EFF;
    transform: translateY(-3px);
    box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);

    &:after {
      content: '✓';
      position: absolute;
      top: 4px;
      right: 4px;
      width: 16px;
      height: 16px;
      background-color: #409EFF;
      color: white;
      border-radius: 50%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 10px;
      z-index: 10;
    }
  }
}

.file-preview {
  height: 120px;
  display: flex;
  align-items: center;
  justify-content: center;
  background-color: #f5f7fa;
  overflow: hidden;

  .el-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  i {
    font-size: 40px;
    color: #909399;
  }
}

.audio-preview, .video-preview, .other-preview {
  flex-direction: column;
  gap: 10px;
}

.audio-player, .video-player {
  width: 130px;
  height: 30px;
  margin-top: 8px;
}

.file-info {
  padding: 10px;
  position: relative;
}

.file-name {
  font-size: 13px;
  margin-bottom: 4px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.file-meta {
  display: flex;
  justify-content: space-between;
  font-size: 12px;
  color: #909399;
}

.file-actions {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  gap: 10px;
  padding: 5px;
  background-color: rgba(255, 255, 255, 0.9);
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.3s;

  i {
    cursor: pointer;
    font-size: 16px;
    color: #606266;

    &:hover {
      color: #409EFF;
    }
  }

  .el-icon-delete:hover {
    color: #F56C6C;
  }
}

.no-files {
  width: 100%;
  height: 300px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #909399;

  i {
    font-size: 60px;
    margin-bottom: 20px;
  }

  p {
    font-size: 16px;
  }
}

.upload-container {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 100%;
}

.page-box {
  margin-top: 20px;
  text-align: right;
}

.upload-progress {
  margin-top: 20px;
  max-height: 200px;
  overflow-y: auto;
  
  .progress-item {
    margin-bottom: 15px;
    padding: 10px;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    background-color: #fafafa;
    
    .progress-info {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 8px;
      
      .filename {
        font-weight: 500;
        color: #303133;
        flex: 1;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        margin-right: 10px;
      }
      
      .status {
        font-size: 12px;
        padding: 2px 6px;
        border-radius: 3px;
        
        &.uploading {
          color: #409eff;
          background-color: #ecf5ff;
        }
        
        &.success {
          color: #67c23a;
          background-color: #f0f9ff;
        }
        
        &.error {
          color: #f56c6c;
          background-color: #fef0f0;
        }
      }
    }
  }
}
</style>
